home *** CD-ROM | disk | FTP | other *** search
- title TALK --- Simple terminal emulator
- page 55,132
- .lfcond ;list false conditionals too
-
- ; TALK.ASM --- Simple terminal emulator program
- ; that demonstrates use of the DOS 5 Task Switcher API.
- ; Copyright (c) 1991 Ray Duncan
- ;
- ; Set the equates COM1 and ECHO to TRUE or FALSE as appropriate
- ; for your machine. This program does not set baud rates or
- ; other communications parameters; use the MODE command instead.
- ; Exit from TALK.EXE by entering Alt-X.
-
- stdout equ 1 ; standard output handle
-
- cr equ 0dh ; ASCII carriage return
- lf equ 0ah ; ASCII line feed
-
- AltX equ 02dh ; keycode for Alt-X
-
- bufsiz equ 4096 ; size of serial port buffer
-
- true equ -1
- false equ 0
-
- com1 equ false ; Use COM1 if nonzero
- com2 equ not com1 ; Use COM2 if nonzero
- echo equ false ; FALSE=full-duplex,
- ; TRUE=half-duplex
-
- pic_mask equ 21h ; 8259 int. mask port
- pic_eoi equ 20h ; 8259 EOI port
-
- if com1
- com_data equ 03f8h ; port assignments for COM1
- com_ier equ 03f9h
- com_mcr equ 03fch
- com_sts equ 03fdh
- com_int equ 0ch ; COM1 interrupt number
- int_mask equ 10h ; IRQ4 mask for 8259
- endif
-
- if com2
- com_data equ 02f8h ; port assignments for COM2
- com_ier equ 02f9h
- com_mcr equ 02fch
- com_sts equ 02fdh
- com_int equ 0bh ; COM2 interrupt number
- int_mask equ 08h ; IRQ3 mask for 8259
- endif
-
- _DATA segment word public 'DATA'
-
- in_char db 0 ; PC keyboard input char.
- in_flag db 0 ; <>0 if char waiting
-
- msg1 db cr,lf,'Terminal emulator running...',cr,lf
- msg1_len equ $-msg1
-
- msg2 db cr,lf,'Exit from terminal emulator.',cr,lf
- msg2_len equ $-msg2
-
- oldcomm dd 0 ; original contents of serial
- ; port interrupt vector
-
- switcher dd 0 ; address of task switcher's
- ; service entry point
-
- swvers dd 0 ; pointer to SWVERSION structure
- ; returned by task switcher
-
- cbinfo label word ; switcher callback info structure
- cbnext dd 0 ; addr of next structure in chain
- cbentry dd callback ; entry point for notify function
- dd 0 ; reserved
- cbapi dd apiinfo ; addr of list of api info strucs
-
- apiinfo dw 0 ; empty list of api info structures
-
- hooked dw 0 ; nonzero if we are hooked into
- ; switcher notification chain
-
- asc_in dw 0 ; input pointer to ring buffer
- asc_out dw 0 ; output pointer to ring buffer
-
- asc_buf db bufsiz dup (?) ; communications buffer
-
- _DATA ends
-
- _TEXT segment word public 'CODE'
-
- assume cs:_TEXT,ds:_DATA,ss:_STK
-
- talk proc far
-
- mov ax,_DATA ; make our data addressable
- mov ds,ax
- mov es,ax
-
- call hook ; try and hook switcher
- jc talk1 ; jump, function failed
- mov hooked,-1 ; set flag that we are chained
-
- talk1: call asc_enb ; enable communications port
- mov dx,offset msg1 ; display message
- mov cx,msg1_len ; 'Terminal emulator running'
- mov bx,stdout ; BX = standard output handle
- mov ah,40h ; Fxn 40H = write file or device
- int 21h ; transfer to MS-DOS
-
- talk2: call pc_stat ; keyboard character waiting?
- jz talk4 ; nothing waiting, jump
- call pc_in ; read keyboard character
- cmp al,0 ; is it a function key?
- jne talk3 ; not function key, jump
- call pc_in ; function key, get 2nd part
- cmp al,AltX ; was it Alt-X?
- je talk5 ; yes, terminate program
- push ax ; no, send to comm port
- xor al,al ; send lead byte (0)
- call com_out
- pop ax ; send function key code
- call com_out
- jmp talk2 ; get another key
-
- talk3: if echo ; keyboard character received
- push ax ; if half-duplex, echo
- call pc_out ; character to PC display
- pop ax
- endif
- call com_out ; send char to serial port
-
- talk4: call com_stat ; serial port input waiting?
- jz talk2 ; nothing waiting, jump
- call com_in ; read char from serial port
- call pc_out ; send it to PC display
- jmp talk4 ; see if any more waiting
-
- talk5: call asc_dsb ; release comm hardware
- cmp hooked,0 ; did we hook task switcher?
- je talk6 ; no, jump
- call unhook ; release switcher hooks
-
- talk6: mov dx,offset msg2 ; display farewell message
- mov cx,msg2_len ; DX=addr, CX=length
- mov bx,stdout ; BX = standard output handle
- mov ah,40h ; Fxn 40H = write device
- int 21h ; transfer to MS-DOS
-
- mov ax,4c00h ; terminate program with
- int 21h ; return code = 0
-
- talk endp
-
- ; The variable OLDMUX holds the previous contents of the Int 2FH
- ; vector. The variable is in the code segment so that it is
- ; addressable at interrupt time without changing DS.
- oldmux dd 0 ; prev owner of Int 2FH
-
- ;
- ; HOOK - Hook switcher notification chain and multiplex interrupt.
- ;
- ; Call with: Nothing
- ; Returns: Carry = clear if switcher was hooked
- ; Carry = set if switcher not present or could
- ; not be hooked.
- ; Destroys: AX, BX, CX, DX, DI, ES
- ;
- hook proc near
-
- xor bx,bx ; check if switcher present
- mov di,bx ; BX, ES:DI must be zero
- mov es,bx
- mov ax,4b02h ; multiplex function 4b02h
- int 2fh ; is detect switcher call
-
- mov ax,es ; check for ES:DI=0
- or ax,di
- jz hook2 ; jump if no switcher
-
- mov word ptr switcher,di ; save address of switcher
- mov word ptr switcher+2,es ; service entry point
- xor ax,ax ; get pointer to switcher's
- call [switcher] ; version data structure
- jc hook2 ; jump, function failed
-
- mov word ptr swvers,bx ; otherwise pointer was
- mov word ptr swvers+2,es ; returned in ES:BX
- cmp word ptr es:[bx],1 ; check switcher protocol version
- jne hook2 ; we insist on version 1.0 or we
- cmp word ptr es:[bx+2],0 ; don't hook notification chain
- jne hook2 ; jump, not 1.0
-
- mov ax,4 ; now hook notification chain
- mov di,ds ; switcher function = 4
- mov es,di ; ES:DI = addr of switcher
- mov di,offset _DATA:cbinfo ; callback info structure
- call [switcher] ; call switcher entry point
- jc hook2 ; jump, function failed
-
- mov ax,352fh ; get current vector for
- int 21h ; multiplex interrupt 2FH
- mov word ptr oldmux+2,es ; and save it
- mov word ptr oldmux,bx
-
- push ds ; save our data segment
- mov ax,cs ; set DS:DX = address
- mov ds,ax ; of our multiplex handler
- mov dx,offset muxisr ; to hook Int 2FH
- mov ax,252fh ; Fxn 25H = set vector
- int 21h ; transfer to MS-DOS
- pop ds ; restore data segment
- clc ; return carry = clear
- ret ; if switcher was hooked
-
- hook2: stc ; return carry = set
- ret ; if switcher not hooked
-
- hook endp
-
- ;
- ; UNHOOK - Release switcher notification chain and multiplex interrupt.
- ;
- ; Call with: Nothing
- ; Returns: Nothing
- ; Destroys: AX, DX, DI, ES
- ;
- unhook proc near
-
- push ds ; save our data segment
- lds dx,oldmux ; load address of previous
- ; multiplex interrupt owner
- mov ax,252fh ; Fxn 25H = set vector
- int 21h ; transfer to MS-DOS
- pop ds ; restore data segment
-
- mov ax,5 ; unhook from notify chain
- mov di,ds ; switcher function = 5
- mov es,di ; ES:DI = address of
- mov di,offset _DATA:cbinfo ; callback info structure
- call [switcher] ; call switcher entry point
- ret
- unhook endp
-
- ;
- ; MUXISR - Handler for multiplex interrupt.
- ;
- ; Call with: AH = multiplex number, other registers vary.
- ; Returns: ES:BX = address of callback info structure
- ; if multiplex function 4BH subfunction 01H detected,
- ; otherwise changes nothing, chains to previous owner.
- ; Destroys: Nothing
- ;
- muxisr proc far
-
- cmp ax,4b01h ; is this Build Notify Chain?
- je mux2 ; yes, process it
- jmp [oldmux] ; no, chain to old handler
-
- mux2: push ds ; save updated switcher
- push ax ; entry point address
- mov ax,_DATA
- mov ds,ax
- mov word ptr switcher+2,cx
- mov word ptr switcher,dx
- pop ax
- pop ds
- pushf ; call down through the
- call [oldmux] ; multiplex handler chain
- push ds ; now save address of next
- push ax ; guy's callback info structure
- mov ax,_DATA ; in our callback info structure
- mov ds,ax
- mov word ptr cbnext,bx
- mov word ptr cbnext+2,es
- mov es,ax ; let ES:BX = address of
- mov bx,offset _DATA:cbinfo ; our callback info structure
- pop ax ; and pass it back to switcher
- pop ds
- iret ; return from multiplex interrupt
-
- muxisr endp
-
- ;
- ; CALLBACK - Switcher callback function installed by HOOK and MUXISR.
- ; Looks for session switch notifications and refuses same.
- ;
- ; Call with: AX = notification type, other registers vary.
- ; Returns: AX <> 0 if Query Suspend Session or Suspend Session
- ; notifications, otherwise AX = 0.
- ; Destroys: Nothing
- ;
- callback proc far
-
- cmp ax,1 ; is it Query Suspend callback?
- je callb2 ; yes, return 1 to block suspend
- cmp ax,2 ; is it Suspend Session callback?
- je callb2 ; yes, return 1 to block suspend
- xor ax,ax ; else return success
-
- callb2: retf
-
- callback endp
-
- ;
- ; COM_STAT - Check communications port status
- ;
- ; Call with: Nothing
- ; Returns: Zero flag = false if character ready
- ; Zero flag = true if no character waiting
- ; Destroys: Nothing
- ;
- com_stat proc near
-
- push ax ; save AX
- mov ax,asc_in ; compare ring buffer pointers
- cmp ax,asc_out ; to set Zero flag
- pop ax ; restore AX
- ret ; return to caller
-
- com_stat endp
-
- ;
- ; COM_IN - Get character from serial port
- ;
- ; Call with: Nothing
- ; Returns: AL = character
- ; Destroys: Nothing
- ;
- com_in proc near
-
- push bx ; save register BX
-
- com_in1: ; if no char waiting, wait
- mov bx,asc_out ; until one is received
- cmp bx,asc_in
- je com_in1 ; jump, nothing waiting
- mov al,[bx+asc_buf] ; extract char from buffer
- inc bx ; update buffer pointer
- cmp bx,bufsiz ; time to wrap pointer?
- jne com_in2 ; no, jump
- xor bx,bx ; yes, reset pointer
- com_in2:
- mov asc_out,bx ; store updated pointer
- pop bx ; restore register BX
- ret ; and return to caller
-
- com_in endp
-
- ;
- ; COM_OUT - Transmit character to serial port
- ;
- ; Call with: AL = character
- ; Returns: Nothing
- ; Destroys: Nothing
- ;
- com_out proc near
-
- push dx ; save register DX
- push ax ; save character to send
- mov dx,com_sts ; DX = status port address
-
- com_out1: ; check if transmit buffer
- in al,dx ; is empty (TBE bit = set)
- and al,20h
- jz com_out1 ; no, must wait
- pop ax ; retrieve character
- mov dx,com_data ; DX = data port address
- out dx,al ; transmit the character
- pop dx ; restore register DX
- ret ; and return to caller
-
- com_out endp
-
- ;
- ; PC_STAT - Get keyboard status
- ;
- ; Call with: Nothing
- ; Returns: Zero flag = false if character ready
- ; Zero flag = true if no character waiting
- ; Destroys: DX
- ;
- pc_stat proc near
-
- mov al,in_flag ; if character already
- or al,al ; waiting, return status
- jnz pc_stat1 ; in Zero flag
- mov ah,6 ; otherwise call DOS to
- mov dl,0ffh ; determine keyboard status
- int 21h
- jz pc_stat1 ; jump if no key ready
- mov in_char,al ; got key, save it for
- mov in_flag,0ffh ; "pc_in" routine
-
- pc_stat1: ; return to caller with
- ret ; status in Zero flag
-
- pc_stat endp
-
- ;
- ; PC_IN - Get character from keyboard
- ;
- ; Call with: Nothing
- ; Returns: AL = character
- ; Destroys: DX
- ;
- pc_in proc near
-
- mov al,in_flag ; key already waiting?
- or al,al
- jnz pc_in1 ; yes,return it to caller
- call pc_stat ; no, try and read char.
- jmp pc_in
- pc_in1: mov in_flag,0 ; clear char. waiting flag
- mov al,in_char ; and return AL = char.
- ret
-
- pc_in endp
-
- ;
- ; PC_OUT - Send character to PC screen
- ;
- ; Call with: AL = character
- ; Returns: Nothing
- ; Destroys: AX, DX
- ;
- pc_out proc near
-
- mov dl,al ; DL = char to output
- mov ah,6 ; function 6 = direct I/O
- int 21h ; transfer to MS-DOS
- ret ; return to caller
-
- pc_out endp
-
- ;
- ; ASC_ENB - Enable comm port, capture communications interrupt
- ;
- ; Call with: Nothing
- ; Returns: Nothing
- ; Destroys: AX, BX, DX, ES
- ;
- asc_enb proc near
-
- mov ax,3500h+com_int ; save address of previous
- int 21h ; owner of comm interrupt
- mov word ptr oldcomm+2,es ; function 35H returns
- mov word ptr oldcomm,bx ; vector in ES:BX
-
- ; install our comm handler
- push ds ; save our data segment
- mov ax,cs ; DS:DX = address of
- mov ds,ax ; comm interrupt handler
- mov dx,offset asc_isr
- mov ax,2500h+com_int ; Fxn 25H = set vector
- int 21h ; transfer to MS-DOS
- pop ds ; restore data segment
-
- mov dx,com_mcr ; set modem control reg.
- mov al,0bh ; DTR and OUT2 bits
- out dx,al ; to enable interrupts
-
- mov dx,com_ier ; set interrupt enable
- mov al,1 ; register on serial
- out dx,al ; port controller
-
- in al,pic_mask ; read 8259 interrupt mask
- and al,not int_mask ; set mask for COM port
- out pic_mask,al ; write new 8259 mask
-
- ret ; back to caller
-
- asc_enb endp
-
- ;
- ; ASC_DSB - Release communications interrupt, disable comm hardware
- ;
- ; Call with: Nothing
- ; Returns: Nothing
- ; Destroys: AX, DX
- ;
- asc_dsb proc near
-
- in al,pic_mask ; read 8259 interrupt mask
- or al,int_mask ; reset mask for COM port
- out pic_mask,al ; write new 8259 mask
-
- push ds ; save our data segment
- lds dx,oldcomm ; DX:DX = address of prev
- ; comm interrupt handler
- mov ax,2500h+com_int ; Fxn 25H = set vector
- int 21h ; transfer to MS-DOS
- pop ds ; restore data segment
-
- ret ; back to caller
-
- asc_dsb endp
-
- ;
- ; ASC_ISR - Communications interrupt service routine
- ; Call with: Nothing
- ; Returns: Nothing
- ; Destroys: Nothing
- ;
- asc_isr proc far
-
- sti ; turn interrupts back on
- push ax ; save registers
- push bx
- push dx
- push ds
-
- mov ax,_DATA ; make our data segment
- mov ds,ax ; addressable
-
- mov dx,com_data ; DX = data port address
- in al,dx ; read this character
- mov bx,asc_in ; get buffer pointer
- mov [asc_buf+bx],al ; store this character
- inc bx ; bump pointer
- cmp bx,bufsiz ; time for wrap?
- jne asc_isr1 ; no, jump
- xor bx,bx ; yes, reset pointer
-
- asc_isr1: ; store updated pointer
- mov asc_in,bx
-
- mov al,20h ; send EOI to 8259
- out pic_eoi,al
-
- pop ds ; restore all registers
- pop dx
- pop bx
- pop ax
- iret ; return from interrupt
-
- asc_isr endp
-
- _TEXT ends
-
- _STK segment para stack 'STACK'
-
- db 128 dup (?)
-
- _STK ends
-
- end talk
-